// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod ros;

use std::{fmt::Write, sync::Arc};

use lazy_static::lazy_static;
use ros::{get_search_paths, scan_msg_defs};
use tokio::sync::Mutex;

use ohmw_codegen::msg_def::MsgDefMap;

lazy_static! {
    pub static ref SYSTEM_HANDLES: Arc<Mutex<SystemHandles>> = Arc::new(Mutex::new(SystemHandles::default()));
}

#[derive(Default)]
pub struct SystemHandles {
    pub msg_defs: MsgDefMap,
    msg_scanned: bool,
}

impl SystemHandles {
    pub fn try_init_ros() -> Result<(), rosrust::error::Error> {
        rosrust::try_init("maestro")
    }

    pub fn is_ros_initialized() -> bool {
        rosrust::is_initialized()
    }

    pub fn query_topic_msg_type(topic_name: &str) -> Option<String> {
        debug_assert!(Self::is_ros_initialized());
        let topics = rosrust::topics().ok()?;
    
        topics
            .into_iter()
            .find(|t| t.name == topic_name)
            .map(|t| t.datatype)
    }

    pub fn ensure_ros_initialized() -> bool {
        if Self::is_ros_initialized() {
            return true;
        }

        match Self::try_init_ros() {
            Ok(_) => true,
            Err(e) => {
                log::error!("Failed to initialize ROS: {}", e);
                false
            }
        }
    }

    pub async fn ensure_msg_defs_scanned(&mut self) -> bool {
        if self.msg_scanned {
            return true;
        }
    
        match scan_msg_defs(get_search_paths()).await {
            Ok(msg_defs) => {
                self.msg_defs = msg_defs;
                self.msg_scanned = true;
                true
            },
            Err(e) => {
                log::error!("Failed to scan message definitions: {}", e);
                false
            }
        }
    }
}

pub fn dump_msg_defs<W: Write>(msg_defs: &MsgDefMap, mut w: W) -> Result<(), std::fmt::Error> {
    for (pkg_name, msg_defs) in &msg_defs.map {
        writeln!(w, "Package: {}", pkg_name)?;
        for (msg_name, _) in msg_defs {
            writeln!(w, "  Message: {}", msg_name)?;
        }
    }
    Ok(())
}
